package main

import (
	"fmt"
	"sync"
	"time"
)

//上例的互斥锁，是傻瓜式的。但也可能造成资源的浪费
//RWMutex 读写锁。它将对资源的加锁 解锁，分得更细，提高了效率。

//当只加了读锁时，从线程读数据，不会阻塞
//加了写锁后,无法再加其它锁直到写锁释放

func main() {
	lock := &sync.RWMutex{} //实例化读写锁
	lock.Lock()             //加写锁
	fmt.Println("已加写锁")

	for i := 0; i < 4; i++ {
		go func(i int) {
			fmt.Printf("第 %d 个协程准备开始... \n", i)
			lock.RLock() //加读锁（因为前面已加了写锁，所以这里会阻塞一会）
			fmt.Printf("第 %d 个协程获得读锁, sleep 1s 后，释放锁\n", i)
			time.Sleep(time.Second)
			lock.RUnlock() //释放读锁
		}(i)
	}

	fmt.Println("5秒后释放写锁，读锁不再阻塞")
	time.Sleep(time.Second * 5)
	// 写锁一释放，读锁就自由了
	lock.Unlock()

	// 由于会等到读锁全部释放，才能获得写锁
	// 因此这里一定会在上面 4 个协程全部完成才能往下走

	fmt.Println("协程中所有读锁全部释放，就能加写锁了")
	lock.Lock()
	fmt.Println("程序退出...")
	lock.Unlock()
}
